Skip to content

Fix getcwd() failed error in subshells and directory operations (#44)#105

Merged
konard merged 10 commits into
mainfrom
issue-44-796ff0a8
Jun 10, 2026
Merged

Fix getcwd() failed error in subshells and directory operations (#44)#105
konard merged 10 commits into
mainfrom
issue-44-796ff0a8

Conversation

@konard

@konard konard commented Sep 9, 2025

Copy link
Copy Markdown
Member

Summary

Fixes issue #44 where getcwd() system call failures would crash command-stream during subshell execution. process.cwd() throws getcwd() failed: No such file or directory when the current working directory has been deleted or becomes inaccessible — common in CI/CD pipelines that run inside temporary directories that get cleaned up.

This PR merges the latest main (which restructured the repo into the js/ + rust/ monorepo layout) and re-applies the getcwd() fix to the new modular ProcessRunner architecture.

Root cause

In the refactored architecture, subshell execution lives in js/src/$.process-runner-orchestration.mjs. The _runSubshell method called const savedCwd = process.cwd(); with no error handling. When getcwd() fails, this throws and the whole command rejects:

Error: getcwd() failed: No such file or directory
    at _runSubshell (js/src/$.process-runner-orchestration.mjs:165:30)

Changes

  • safeCwd() helper — returns null instead of throwing when process.cwd() fails.
  • _runSubshell — captures the directory via safeCwd(), so it no longer crashes when getcwd() fails.
  • restoreCwd() — handles a null saved directory and falls back to a safe location (HOMEUSERPROFILE/) when the original directory is gone or chdir fails.
  • _runSimpleCommand — falls back to the inherited cwd when getcwd() is unavailable.
  • Changesetpatch bump.

Reproduction

import { $ } from 'command-stream';

// Simulate getcwd() failing (deleted working directory)
process.cwd = () => { throw new Error('getcwd() failed: No such file or directory'); };

await $`(echo "hello")`;
// Before: throws "getcwd() failed"
// After:  prints "hello", code 0

Test coverage

js/tests/getcwd-error-handling.test.mjs is a genuine regression test — it fails without the fix and passes with it:

$ bun test js/tests/getcwd-error-handling.test.mjs
✅ 4 pass, 0 fail   (with fix)
❌ 3 pass, 1 fail   (without fix — confirms reproduction)

It covers: process.cwd() always failing, failing only inside _runSubshell, repeated commands after failures, and a real deleted-working-directory scenario.

The full JS suite passes except for pre-existing jq-not-installed environmental failures (same on main).

Verification

  • ✅ Merged latest main, conflicts resolved (repo restructure: src/js/src/, tests/js/tests/)
  • bun run lint, prettier --check, jscpd all clean
  • ✅ cd/cwd test suites pass (54 tests), main suite passes (85 tests)

🤖 Generated with Claude Code


Resolves #44

Adding CLAUDE.md with task information for AI processing.
This file will be removed when the task is complete.

Issue: #44
@konard konard self-assigned this Sep 9, 2025
konard and others added 2 commits September 9, 2025 20:35
This change addresses issue #44 where getcwd() system call failures would cause
command-stream to crash in certain scenarios, particularly when the current directory
has been deleted or permissions have changed.

Changes:
- Added error handling around process.cwd() calls in _runSubshell method
- Improved directory restoration logic with comprehensive fallback handling
- Added graceful handling when original directory cannot be obtained
- Enhanced error logging with better context information

The fix ensures that:
- Commands continue to work even when getcwd() fails
- Directory restoration gracefully falls back to safe locations (HOME, /, etc.)
- No unhandled exceptions are thrown due to directory access issues
- CI/CD environments with temporary directories work reliably

Tests added:
- Comprehensive test cases for getcwd() error scenarios
- Subshell-specific error handling tests
- Directory restoration error handling tests
- Debug examples for reproducing the issue

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@konard konard changed the title [WIP] getcwd() failed error in certain scenarios Fix getcwd() failed error in subshells and directory operations (#44) Sep 9, 2025
@konard konard marked this pull request as ready for review September 9, 2025 17:42
@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

We need get latest changes from default branch, resolve conflicts, and make sure we reproduce, test these case, and if they really fail - fix them.

@konard konard marked this pull request as draft June 10, 2026 14:05
@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

🤖 AI Work Session Started

Starting automated work session at 2026-06-10T14:05:04.288Z

The PR has been converted to draft mode while work is in progress.

This comment marks the beginning of an AI work session. Please wait for the session to finish, and provide your feedback.

konard and others added 2 commits June 10, 2026 14:08
# Conflicts:
#	js/examples/debug-getcwd-error.mjs
#	js/examples/debug-subshell-getcwd.mjs
#	js/tests/getcwd-error-handling.test.mjs
#	src/$.mjs
Re-apply the getcwd() error handling to the modular ProcessRunner after the
monorepo restructure. process.cwd() throws "getcwd() failed" when the current
directory has been deleted or becomes inaccessible (common in CI/CD with
temporary directories).

- Add safeCwd() helper that returns null instead of throwing
- _runSubshell captures the cwd via safeCwd() so it no longer crashes
- restoreCwd() falls back to a safe location when the saved dir is gone/null
- _runSimpleCommand falls back to the inherited cwd when getcwd() fails
- Rewrite tests to genuinely reproduce the bug (fails without the fix)
- Add changeset

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@konard konard marked this pull request as ready for review June 10, 2026 14:17
@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

Addressed the feedback:

  • Merged latest main and resolved all conflicts from the monorepo restructure (src/js/src/, tests/js/tests/, examples/js/examples/). The PR is now MERGEABLE / CLEAN.
  • Reproduced the bug on the new architecture. _runSubshell in js/src/$.process-runner-orchestration.mjs called process.cwd() with no error handling, so a getcwd() failed throw crashed the whole command (stack confirmed at $.process-runner-orchestration.mjs:165).
  • Re-applied the fix to the modular ProcessRunner (safeCwd() helper + robust restoreCwd() fallback + _runSimpleCommand fallback).
  • Rewrote the test into a genuine regression test — it fails without the fix (1 fail) and passes with it (4 pass). The previous test passed even without the fix, so it didn't actually guard against the bug.
  • Added a changeset; lint, format:check, and jscpd all pass. Remaining suite failures are pre-existing jq: not found environmental issues (identical on main).

@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $2.941017

📊 Context and tokens usage:

  • 81.8K / 1M (8%) input tokens, 27.0K / 128K (21%) output tokens

Total: (3.6K new + 68.9K cache writes + 3.6M cache reads) input tokens, 27.0K output tokens, $2.941017 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.8 (claude-opus-4-8)

📎 Log file uploaded as Gist (1666KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

🔄 Auto-restart triggered (iteration 1)

Reason: CI failures detected

Starting new session to address the issues.


Auto-restart-until-mergeable mode is active. This run will stop after 5 restart iterations.

@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

⏳ Usage Limit Reached

The automated solution draft was interrupted because the Anthropic Claude Code usage limit was reached.

📊 Limit Information

  • Tool: Anthropic Claude Code
  • Limit Type: Usage limit exceeded
  • Reset Time: in 2h 8m (Jun 10, 4:50 PM UTC)
  • Session ID: 2f40e6fb-e2fc-43cc-bece-2e5e1e2e6b84

🔄 How to Continue

Auto-restart is enabled. The session will automatically restart (fresh start) when the limit resets.

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.8 (claude-opus-4-8)

📎 Execution log uploaded as Gist (3897KB)


This session was interrupted due to usage limits. The session will automatically restart when the limit resets.

konard added 3 commits June 10, 2026 17:01
The previous getcwd() fix prevented process.cwd() from throwing, but a
child process spawned with an inherited (deleted) working directory still
failed at the OS level with 'posix_spawn ENOENT' on Linux/Windows. This
caused the 'subshell runs even when the real working directory was
deleted' regression test to fail on those platforms.

- Add resolveSpawnCwd() in $.shell.mjs: when no explicit cwd is given and
  process.cwd() fails, spawn from a valid fallback directory instead of
  inheriting the broken one. Applied in both async and sync spawn paths.
- Mirror the change in the Rust implementation (resolve_spawn_cwd in
  lib.rs, used by ProcessRunner and Pipeline) to satisfy JS/Rust parity.
- Add a Rust regression test (rust/tests/getcwd_error_handling.rs).
- Keep $.process-runner-execution.mjs under the 1500-line lint limit by
  housing the helper in $.shell.mjs.
@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

🔄 Auto-restart triggered (iteration 2)

Reason: CI failures detected

Starting new session to address the issues.


Auto-restart-until-mergeable mode is active. This run will stop after 5 restart iterations.

konard added 2 commits June 10, 2026 17:14
process.cwd() does not reliably throw when the working directory has
been deleted; under Bun it returns a stale path that no longer exists.
resolveSpawnCwd() now validates the directory with fs.existsSync()
instead of relying on process.cwd() to throw, and falls back to a
known-good directory when an explicit cwd is missing or gone. Fixes the
CI failure in the deleted-working-directory regression test on Linux.
Windows locks the current working directory, so fs.rmSync on the cwd
throws EBUSY and the deleted-working-directory scenario cannot occur.
Skip the regression test on Windows, matching the Rust suite.
@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

Working session summary

All checks pass. The two previously-failing CI checks are now green.

Summary

The PR for issue #44 had two failing CI checks (Test JavaScript (bun on ubuntu-latest) and Test JavaScript (bun on windows-latest)). Both were caused by the new deleted-working-directory regression test, not the library logic, but the Linux failure exposed a genuine gap in the fix. Two root causes, two fixes:

1. Linux/ubuntu — real bug in resolveSpawnCwd (js/src/$.shell.mjs)
The original fix only fell back to a safe directory when process.cwd() threw. But under Bun, process.cwd() returns a stale path for a deleted directory without throwing (verified: cwd: /tmp/cwdtest-XHv0ez exists: false). The child then inherited that non-existent cwd and posix_spawn '/bin/sh' failed with ENOENT.

The fix validates the directory with fs.existsSync() instead of relying on process.cwd() to throw — for both explicit cwd and the inherited-cwd case — falling back to a known-good directory (HOMEUSERPROFILE → tmp → /). Normal inherit semantics are preserved when the cwd is valid.

2. Windows — invalid test scenario
Windows locks the current working directory, so fs.rmSync on the cwd throws EBUSY — the "deleted working directory" scenario simply cannot occur there. Skipped that single test on Windows, matching how the Rust suite already skips its equivalent (commit 653ddb3). The other three getcwd tests still run on Windows.

Verification: All 21 CI checks pass — Test JavaScript (bun on ubuntu-latest), bun on windows-latest, bun on macos-latest, all Node and Rust jobs green, zero failures.

PR: #105


This summary was automatically extracted from the AI working session output.

@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

🔄 Auto-restart-until-mergeable Log (iteration 2)

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $1.946130

📊 Context and tokens usage:

  • 67.3K / 1M (7%) input tokens, 15.9K / 128K (12%) output tokens

Total: (3.8K new + 54.3K cache writes + 2.4M cache reads) input tokens, 15.9K output tokens, $1.946130 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.8 (claude-opus-4-8)

📎 Log file uploaded as Gist (6283KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard

konard commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

@konard konard merged commit fe8aae7 into main Jun 10, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

getcwd() failed error in certain scenarios

1 participant